#pragma once
#include <QtOpenGL/QGLWidget>
#include <GL/glu.h>
#include "VisTypes.h"
#include "ExaminerCameraController.h"
#include "Camera.h"
#include "ViewFrustum.h"
#include "viewers\shader.h"
#include "vbo.h"
#include "camera3d.h"
#include <QtGui\qgraphicsscene.h>
#include <QtGui\QGraphicsView.h>
#include <QtGui\qpixmap.h>
#include <QtGui\QGraphicsPixmapItem>


struct VertexPositionNormal {
	Vector3f position;
	Vector3f normal;
};

struct Model {
	std::vector<VertexPositionNormal> vertices;
	std::vector<unsigned int> indices;

	VertexBuffer m_vertices;
	ElementBuffer m_indices;

	//std::vector<Vector3f> vertex_normals;
};

struct AAMode {
	bool enabled;
	int nRaysPerPixel;
	
	bool useGaussianWeighting;
	float gaussianWeightingSigma;

	bool useDivideInSubPixel;
	bool useSubPixelJitter;
};

#include "bvh.h"

struct Ray {
	Vector3f offset;
	Vector3f normal;
	bool in_medium;
	float refraction_index;
};

struct RaytraceResult {
	Vector3f location;
	Vector3f normal;
	Vector3f color;
	float fraction;
	bool hit;
	unsigned int object_id;
};

struct Material {
	Material() : reflection_component(0.f), refraction_component(0.f), ambient_component(.3f), phong_component(0.7f) {
		color = makeVector3f(0.7f, 0.7, 0.7f);
		colorize = true;
		reflection_glossiness = 1.0f;
		emiting = false;
		brightness = 1.0f;
	}

	bool emiting;
	float brightness;

	bool colorize;
	Vector3f color;
	float reflection_component;
	float refraction_component; 
	float ambient_component;
	float phong_component;
	float reflection_glossiness;
};

class GFXWidget3D : public QGLWidget {
   Q_OBJECT
public:
	GFXWidget3D(QWidget *parent=0, const QGLWidget *shareWidget=0, Qt::WindowFlags f=0);
   virtual ~GFXWidget3D();

	virtual void paintGL();
	virtual void initializeGL();
	virtual void resizeGL(int width, int height);
	virtual void mousePressEvent(QMouseEvent *event);
	virtual void mouseReleaseEvent (QMouseEvent * event);
	virtual void mouseMoveEvent(QMouseEvent *event);

	static Vector3f Calculate_triangle_normal(Vector3f v1, Vector3f v2, Vector3f v3);
	void addModel (Model* newModel);
	void UpdateParams (float specular_intensity, bool enable_shadows, bool enable_reflection, bool enable_per_vertex_normals);
	void setColor(Vector3f color);
	void toggleWireframe();
	void raytraceRender(bool use_pathtracer, bool enable_refractions, bool use_bvh, AAMode aa );
	void shade(Ray& ray, RaytraceResult& result, int depth);
	Vector2f phongShader(Ray& ray, RaytraceResult& result);
	float shadowShader(RaytraceResult& result);
	Vector3f reflectShader(Ray& ray, RaytraceResult& result, float glossiness, int depth);
	Vector3f refractionShader(Ray& ray, RaytraceResult&result, int depth, float refraction_index);
	void rayTransformDOF(Ray& ray);

	RaytraceResult pathtrace(Ray& ray, int depth = 1, float min_fraction = 0.01f);

	void pathtraceLambert(RaytraceResult& result, int depth);

	void createAndShowIMG(float intensity_factor);
	bool clearray(Ray& ray);
	RaytraceResult bvhtrace(Ray& ray, BVHNode* node, float min_fraction = 0.0f);
	RaytraceResult raytrace(Ray& ray, float min_fraction = 0.0f, int depth = 0);
	bool raytrace_aabb( Ray& ray, AABB& bb, float min_fraction);
	RaytraceResult raytrace_triangle( Ray& ray, VertexPositionNormal v1, VertexPositionNormal v2, VertexPositionNormal v3);
	RaytraceResult raytrace_floor( Ray& ray, float min_fraction);
	

private:
		VertexBuffer m_vertices;
	ElementBuffer m_indices;

	Material m_materials[10];

	Camera3D camera;
	ViewFrustum vf;

	QGraphicsScene scene;
    QGraphicsView view;
	QGraphicsPixmapItem item;
	QImage img;

	Shader m_shader_3d;
	VertexShader m_vertex_shader;
	FragmentShader m_fragment_shader;

	//int m_mouse_x, m_mouse_y;
	QPoint m_mouse_point;

	Matrix3f m_rotation;
	float m_angle_x, m_angle_y;
	float m_distance;
	bool m_wireframe;
	bool m_enable_shadows;
	bool m_enable_bvh;
	bool m_enable_reflection;
	bool m_per_vertex_normals;
	bool m_glossy_reflections;
	bool m_path_tracing;
	bool m_enable_refraction;
	bool m_enable_dof;
	float m_focal_length;
	float m_aperture;

	Vector3f * m_radiance;
	float m_intensity_factor;

	bool m_next_event;

	Vector3f m_center;

	void updateCam();

	vector<Model*> m_models;
	float m_specular_intensity;
	Vector3f m_lightcolor;

	BVH m_bvh;
};